#pragma  once 
#include<list>
#include<mutex>
#include<condition_variable>
#include"Util.hpp"
#include "online.hpp"
#include "room.hpp"
#include "db.hpp"
#include<thread>

template<class T>
class mathch_queue
{
private: 
  std::list<T> _list;  //阻塞队列 
  std::mutex _mutex;  //互斥锁
  std::condition_variable _cond; //条件变量，消费者<2时等待 

public:
  mathch_queue(){}
  ~mathch_queue(){}

  //获取元素个数
  size_t size(){ 
    std::unique_lock<std::mutex> lock(_mutex);  
    return _list.size(); 
  }

  //判断是否为空 
  bool empty(){  
    std::unique_lock<std::mutex> lock(_mutex);  
    return _list.empty();
  }

  //等待
  void wait(){
    std::unique_lock<std::mutex> lock(_mutex); 
    _cond.wait(lock); //等待 
  }
  
  //唤醒
  void wakeup(){ _cond.notify_all(); }

  //入队 
  void push(const T& in){
    std::unique_lock<std::mutex> lock(_mutex);  
    _list.push_back(in); 
    wakeup();
  }

  //出队
  bool pop(T* out){
    std::unique_lock<std::mutex> lock(_mutex);  
    if(_list.empty())
    {
      return false;
    }
    *out = _list.front();
    _list.pop_front();
    return true; 
  }

  //移除队列数据 
  void remove(T& data){
    std::unique_lock<std::mutex> lock(_mutex);  
    _list.remove(data);
  }
};


class mather
{
private:
  mathch_queue<int> _level_1;  //青铜匹配队列
  mathch_queue<int> _level_2;  //白银匹配队列
  mathch_queue<int> _level_3;  //黄金匹配队列 
  user_table* _ut;  //用户信息管理句柄
  room_manage* _rm ;  //房间管理句柄
  online_manager* _om;  //在线信息管理句柄 
  std::thread _th_level_1; 
  std::thread _th_level_2; 
  std::thread _th_level_3; 

private:

  void handl_match(mathch_queue<int>& mq)
  {
    while(true)
    {
    //1.队列人数是否小于2，阻塞等待
      while(mq.size() < 2)
      {
        mq.wait(); 
      }
      
      //2.从队列中抽取2个玩家
      int uid1,uid2; 
      bool ret = mq.pop(&uid1); 
      if(ret == false)
      {
        //玩家1 出队失败 
        continue; 
      }
      
      ret = mq.pop(&uid2); 
      if(ret == false)
      {
        //玩家2出队失败
        this->add(uid1);//把玩家1重新放回队列
        continue; 
      }

      //3.检验两个玩家是否都在线 
      wsserver_t::connection_ptr con1 = _om->get_conn_from_hall(uid1); 
      wsserver_t::connection_ptr con2 = _om->get_conn_from_hall(uid2);
      if(con1.get() == nullptr)
      {
        //玩家1掉线了
        this->add(uid2); //玩家2重新放回匹配队列 
        continue;
      }
      if(con2.get() == nullptr)
      {
        //玩家2掉线了 
        this->add(uid1); 
        continue; 
      }

      //4创建房间 
      room_ptr rp = _rm->create_room(uid1,uid2); 
      if(rp.get() == nullptr)
      {
        //房间创建失败,两个玩家重新放回队列
        this->add(uid1);
        this->add(uid2); 
        continue;
      }

      //5.对2个玩家进行响应
      Json::Value resp; 
      resp["optype"] = "match_success"; 
      resp["result"] = true; 
      std::string body;
      UtilJson::serialize(resp,body); 
      con1->send(body);
      con2->send(body);
    } 
  }
  void th_level_1_entry(){ handl_match(_level_1); }
  void th_level_2_entry(){ handl_match(_level_2); }
  void th_level_3_entry(){ handl_match(_level_3); }

public: 
  mather(user_table* ut , room_manage* rm , online_manager* om)
  : _ut(ut) , _rm(rm) , _om(om) , _th_level_1(std::thread(&mather::th_level_1_entry,this)) 
  , _th_level_2(std::thread(&mather::th_level_2_entry,this)) 
  , _th_level_3(std::thread(&mather::th_level_3_entry,this))
  {
    DLOG("游戏匹配模块初始化成功");
  }
  ~mather(){}

  bool add(int uid)
  {
    //通过ID获取用户信息 
    Json::Value user; 
    bool ret =  _ut->select_by_id(uid,user); //根据ID获取用户信息
    if(ret == false)
    {
      //查找失败 
      DLOG("用户 %d 信息获取失败",uid);
      return false; 
    }
    //获取分数 
    int score = user["score"].asInt(); 

    //根据分数放进不同的匹配队列 
    if(score < 2000)
    {
      _level_1.push(uid);  
    }else if(score >= 2000 & score < 3000)
    {
      _level_2.push(uid); 
    }else 
    {
      _level_3.push(uid);
    }
    return true;
  }

  bool del(int uid)
  {
    //通过ID获取用户信息 
    Json::Value user; 
    bool ret =  _ut->select_by_id(uid,user); //根据ID获取用户信息
    if(ret == false)
    {
      //查找失败 
      DLOG("用户 %d 信息获取失败",uid);
      return false; 
    }
    //获取分数 
    int score = user["score"].asInt(); 

    //根据分数放进不同的匹配队列 
    if(score < 2000)
    {
      _level_1.remove(uid);  
    }else if(score >= 2000 & score < 3000)
    {
      _level_2.remove(uid); 
    }else 
    {
      _level_3.remove(uid);
    }
    return true; 
  }
};
